/*
 * Rewriter.java
 * 
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements. See the NOTICE file distributed with this
 * work for additional information regarding copyright ownership. The ASF
 * licenses this file to You under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

package com.toolsverse.util;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * A rewriter does a global substitution in the strings passed to its 'rewrite'
 * method. It uses the pattern supplied to its constructor, and is like
 * 'String.replaceAll' except for the fact that its replacement strings are
 * generated by invoking a method you write, rather than from another string.
 * This class is supposed to be equivalent to Ruby's 'gsub' when given a block.
 * This is the nicest syntax I've managed to come up with in Java so far. It's
 * not too bad, and might actually be preferable if you want to do the same
 * rewriting to a number of strings in the same method or class. See the example
 * 'main' for a sample of how to use this class.
 * 
 * @author Elliott Hughes
 * @author Roger Millington
 */

public abstract class Rewriter
{
    private Pattern pattern;
    private Matcher matcher;
    protected StringBuffer buffer;
    
    /**
     * Constructs a rewriter using the given regular expression; the syntax
     * is the same as for 'Pattern.compile'.
     */
    public Rewriter(String regularExpression)
    {
        this.pattern = Pattern.compile(regularExpression);
    }
    
    /**
     * Returns the offset after the last character matched.   
     * @return the offset after the last character matched
     */
    public int end()
    {
        return matcher.end();
    }
    
    /**
     * Returns the input subsequence captured by the given group during the
     * previous match operation.
     */
    public String group(int i)
    {
        return matcher.group(i);
    }
    
    /**
     * Overridden to compute a replacement for each match. Use the method
     * 'group' to access the captured groups.
     */
    public abstract String replacement();
    
    /**
     * Returns the result of rewriting 'original' by invoking the method
     * 'replacement' for each match of the regular expression supplied to the
     * constructor.
     */
    public String rewrite(CharSequence original)
    {
        return rewrite(original, new StringBuffer(original.length()))
                .toString();
    }
    
    /**
     * Returns the result of appending the rewritten 'original' to
     * 'destination'. We have to use StringBuffer rather than the more obvious
     * and general Appendable because of Matcher's interface (Sun bug 5066679).
     * Most users will prefer the single-argument rewrite, which supplies a
     * temporary StringBuffer itself.
     */
    public StringBuffer rewrite(CharSequence original, StringBuffer destination)
    {
        buffer = destination;
        
        this.matcher = pattern.matcher(original);
        while (matcher.find())
        {
            matcher.appendReplacement(destination, "");
            destination.append(replacement());
        }
        matcher.appendTail(destination);
        return destination;
    }
}